//
//  53.表示数值的字符串.swift
//  数据结构与算法
//
//  Created by ZERO on 2021/6/2.
//

import Foundation
/*
 题目：请实现一个函数用来判断字符串是否表示数值（包括整数和小数）。例如，字符串"+100","5e2","-123","3.1416"和"-1E-16"都表示数值。 但是"12e","1a3.14","1.2.3","+-5"和"12e+4.3"都不是。
 思路1：使用正则表达式
 思路2：[sign]integral-digits[.[fractional-digits]][e|E[sign]exponential-digits]
       其中，('['和']'之间的为可有可⽆的部分)。 在数值之前可能有⼀个表示正负的'+'或者'-'。接下来是若⼲个0到9的数位表示数值的整数部分（在某些⼩数⾥可能没有数值的整数部分）。如果数值是 ⼀个⼩数，那么在⼩数后⾯可能会有若⼲个0到9的数位表示数值的⼩数部分。如果数值⽤科学记数法表示，接下来是⼀个'e'或者'E'，以及紧跟着的⼀个整数 （可以有正负号）表示指数。 判断⼀个字符串是否符合上述模式时，⾸先看第⼀个字符是不是正负号。如果是，在字符串上移动⼀个字符，继续扫描剩余的字符串中0到9的数位。如 果是⼀个⼩数，则将遇到⼩数点。另外，如果是⽤科学记数法表示的数值，在整数或者⼩数的后⾯还有可能遇到'e'或者'E'。
 */
func offer_53() {
    print(Solution().isNumeric2("-1E-16"))
}

fileprivate class Solution {
    func isNumeric1(_ str: String) -> Bool {
        let regexStr = #"^[+-]?\d*(?:\.?\d*)?(?:[eE][+-]?)?\d+$"#
        let regex: NSPredicate = NSPredicate(format: "SELF MATCHES %@", regexStr)
        return regex.evaluate(with: str)
    }
    
    func isNumeric2(_ str: String) -> Bool {
        guard !str.isEmpty else { return false }
        var str = Array(str+"\0")
        var numeric = true
        // 跳过正负号
        if str.first == "+" || str.first == "-" {
            str.removeFirst()
        }
        scanDigits(&str)
        if str.first != "\0" {
            if str.first == "." {
                str.removeFirst()
                scanDigits(&str)
                if str.first == "e" || str.first == "E" {
                    numeric = isExponential(&str)
                }
            } else if str.first == "e" || str.first == "E" {
                numeric = isExponential(&str)
            } else {
                numeric = false
            }
        }
        return numeric && str.first == "\0"
    }
    
    // 扫描数字对于合法数字直接跳过
    func scanDigits(_ str: inout [String.Element]) {
        guard !str.isEmpty else { return }
        while str.first != "\0" && str.first! >= "0" && str.first! <= "9" {
            str.removeFirst()
        }
    }
    // 科学计数法表示的数值的结尾部分是否合法
    func isExponential(_ str: inout [String.Element]) -> Bool {
        guard !str.isEmpty else { return false }
        str.removeFirst()
        if str.first == "+" || str.first == "-" {
            str.removeFirst()
        }
        if str.first == "\0" {
            return false
        }
        scanDigits(&str)
        return str.first == "\0" ? true : false
    }
}


